package protocol;

import java.util.Iterator;
import java.util.Scanner;
import java.util.Vector;

import outboxHandler.OutboxHandler;
import tokenizer.StringMessage;


public class IRCprotocol implements Protocol<StringMessage> {
	private User user;
	private IRCserverDB serverDB;
	
	/** ########################################################################## */
	private OutboxHandler connectionToOutbox;
	
	/** ########################################################################## */
	public IRCprotocol(IRCserverDB DB) {
		
		serverDB=DB;
		user=new User("");
		
	}
	
	/** ########################################################################## */
	@Override
	public void register(OutboxHandler connector) {
		connectionToOutbox=connector;
		user.register(connectionToOutbox);
	}
	
	public StringMessage processMessage(StringMessage msg)
	{
		String err = null;
		System.out.println("parsing: " +msg);
		String stringMsg=msg.getMessage();
		System.out.println("and stringMsg is: " + stringMsg);
		Scanner scan=new Scanner(stringMsg);
		scan.useDelimiter(" ");
		String commandMsg;
		String parametersMsg;
		if(scan.hasNext())
			commandMsg=scan.next();
		else
			commandMsg="";
		scan.useDelimiter("\n");
		
		if(scan.hasNext())
		{
			parametersMsg=scan.next();	
			parametersMsg=parametersMsg.substring(1);
		}
		else
			parametersMsg="";
		
		System.out.println("commandMsg is: " + commandMsg);
		switch (commandMsg)
		{
					case "NICK": err = ParseNick(parametersMsg);
					break;
					case "USER": err = ParseUser(parametersMsg);
					break;
					case "QUIT": ParseQuit();
					break;
					case "JOIN": err = ParseJoin(parametersMsg);
					break;
					case "PART": err = ParsePart(parametersMsg);
					break;
					case "NAMES": err = ParseNames(parametersMsg);
					break;
					case "LIST": err = ParseList(parametersMsg);
					break;
					case "KICK" : err = "421 KICK: ERR_UNKNOWNCOMMAND " ;
		}
		
		/** !!!!!!!!!!##########################################################################!!!!!!! */
		if(err!=null)
		{	//try {
				System.out.println(err);
				writeToSelf(err);
		}		
		else
		{	
				System.out.println("err still null!");
			    writeToUsersInChannel(msg.getMessage());
		}	    
				//user.getTokenizer().addBytes(user.getTokenizer().getBytesForMessage(err));
			//} catch (CharacterCodingException e) {
			//	e.printStackTrace();
			//}
		scan.close();
		return null;
	}

	/** !!!!!!!!##########################################################################!!!!!!!!!! */
	private void writeToUsersInChannel(String msg) {
		String userChannel=user.getChannel();
		if(userChannel!=null)
		{	
			msg=user.getNick() +": " + msg +'\n';
			serverDB.writeToChannel(msg,userChannel);
		}	
		else
			writeToSelf("451 you have not registered yet to channel");
	}
	
	/** !!!!!!!!!!!!##########################################################################!!!!!! */
	private void writeToSelf(String msg)
	{
		user.toOutbox(msg+'\n');
	}

	private String ParsePart(String substring) {
		if(substring.equals(""))
			return "461 PART :Not enough parameters";
		else
		{
			if(user.getChannel()!=null && user.getChannel().equals(substring))
			{
				serverDB.getChannels().get(user.getChannel()).remove(user);
				user.setChannel(null);
				return "405";
			}
			else
				return "403 PART "+substring + ": no such channel";
			
			
			}
	}
	
	private String ParseJoin(String substring) {
		/** ########################################################################## */
		if(user.getNick()==null)
			return "451 you have not picked a nickname";
		/** ########################################################################## */
		if(user.getUser()==null)	
			return "451 you have not picked a user name";
		if(substring.equals(""))
			return "461 JOIN :Not enough parameters";
		else
		{
			String userChannel=user.getChannel();
			/** ########################################################################## */
			if(userChannel!=null && userChannel.equals(substring)) 			//user has channel and user channel is the one he is in now
			{
				return ParseNames(substring);			//dont make changes, only return names in channel
			}
			else	
			{
				if(userChannel!=null)			//if user was in some channel before, remove him 
				{
					serverDB.getChannels().get(user.getChannel()).remove(user);
				}
				if(serverDB.getChannels().containsKey(substring))	//if channel exist, sign user to channel
				{	
					user.setChannel(substring);
					serverDB.getChannels().get(substring).add(user);
				}	
				else					//if channel doesnt exist, create it and put user inside
				{
					Vector<User> vec = new Vector<User>();
					vec.add(user);
					serverDB.getChannels().put(substring, vec);
					user.setChannel(substring);
				}
				return ParseNames(substring);
			}
			
		
			
			
		}
	}

	private String ParseUser(String substring) {
		if(substring.equals(""))
				return "461 USER :Not enough parameters";
		else
		{
			if(user.getUser()==null)
			{
				user.setUser(substring);
				return "402 your user name is "+ substring;
			}
			else
				return "462 :You may not reregister";
		}
	}

	private void ParseQuit() {
		user.Quit();
	}

	private String ParseList(String substring) {
		if(substring.equals(""))
		{
		String resp="321 \n";
			Iterator<String> it = serverDB.getChannels().keySet().iterator();
			while(it.hasNext())
			{
				String chan = it.next();
				resp = resp + "322 " + chan +"\n";
			}
			resp = resp + "323 :End of /LIST";
		return resp;
		}
		else
			return ParseNames(substring);
	}

	private String ParseNick(String nick) {
		String err=null;
		/** ########################################################################## */
		if(nick.equals(""))
			return "461 NICK :Not enough parameters";
		synchronized(serverDB.getNicknames())
		{
		if(serverDB.getNicknames().containsKey(nick))
			err = "433 " + nick + " Nickname is already in use";
		else
		{
			user.setNick(nick);
			serverDB.getNicknames().put(nick, user);
			err = "401 your nick is "+ nick;
		}
		}
		System.out.println("finished nick command return: "+ err);
		return err;
		
		
		
	}

	private String ParseNames(String substring) {
		String resp = "353 \n";
		if(substring.equals(""))
		{
			Iterator<String> it = serverDB.getChannels().keySet().iterator();
			while(it.hasNext())
			{
				String channel = it.next();
				/** ########################################################################## */
				Vector<User> usersInChannel=serverDB.getChannels().get(channel);
				if(usersInChannel!=null)
					resp =  resp + channel + usersInChannel.toString()+"\n";
				
			}
		}
		else
		{	
			/** ########################################################################## */
			Vector<User> usersInChannel=serverDB.getChannels().get(substring);
			if(usersInChannel!=null)
				resp = resp + usersInChannel.toString() +"\n";
			else
				return "403 PART "+substring + ": no such channel";
		}	
		resp=resp+"366 :End of /NAMES list";
		return resp;
	}

	@Override
	public boolean isEnd(StringMessage msg) {
		return user.shouldQuit();	
	}
	
	@Override
	public boolean shouldClose() {
		return user.shouldQuit();
	}

	/**
	 * shutdown the protocol because client disconnected
	 */
	public void connectionTerminated() {
		/** ########################################################################## */
		user.Quit();
	}
}

